/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.ScriptRuntimeException;
import cn.easyplatform.cfg.TaskExecuter;
import cn.easyplatform.contexts.Contexts;
import cn.easyplatform.contexts.ExecuteContext;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.engine.cmd.task.BeginCmd;
import cn.easyplatform.engine.cmd.task.NextCmd;
import cn.easyplatform.i18n.I18N;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Lang;
import cn.easyplatform.log.LogManager;
import cn.easyplatform.messages.request.BeginRequestMessage;
import cn.easyplatform.messages.request.NextRequestMessage;
import cn.easyplatform.messages.response.BatchResponseMessage;
import cn.easyplatform.messages.response.PageResponseMessage;
import cn.easyplatform.messages.response.SimpleResponseMessage;
import cn.easyplatform.messages.vos.AbstractPageVo;
import cn.easyplatform.messages.vos.NextVo;
import cn.easyplatform.messages.vos.TaskVo;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.util.MessageUtils;
import cn.easyplatform.util.RuntimeUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
class EngineTaskExecuter implements TaskExecuter {

    private final static Logger _log = LoggerFactory.getLogger(EngineTaskExecuter.class);

    private CommandContext _cc;

    public EngineTaskExecuter(CommandContext cc) {
        this._cc = cc;
    }

    @Override
    public void executeBatch(List<ExecuteContext> list, boolean await) {
        try {
            CountDownLatch latch = await ? new CountDownLatch(list.size()) : null;
            for (ExecuteContext ec : list)
                _cc.getEngineConfiguration().getKernelThreadPoolExecutor().execute(new Executor(latch, ec));
            if (latch != null)
                latch.await();
            list.clear();
        } catch (Exception e) {
            throw Lang.wrapThrow(e);
        }
    }

    private class Executor implements Runnable {

        CountDownLatch _latch;

        ExecuteContext _ctx;

        Executor(CountDownLatch latch, ExecuteContext ec) {
            this._latch = latch;
            this._ctx = ec;
        }

        @Override
        public void run() {
            CommandContext cc = _ctx.getCommandContext();
            LogManager.beginRequest(cc.getEnv().getId(), cc.getUser());
            try {
                Session session = SecurityUtils.getSubject().getSession();
                session.setTimeout(-1);//不过期，不需要heartbeat机制
                cc.setSession(session);
                Contexts.set(CommandContext.class, cc);
                IResponseMessage<?> resp = execute(_ctx);
                if (!resp.isSuccess())
                    throw new ScriptRuntimeException(resp.getCode(), (String) resp.getBody());
            } catch (Exception ex) {
                if (_log.isErrorEnabled()) {
                    if (ex instanceof EasyPlatformWithLabelKeyException) {
                        EasyPlatformWithLabelKeyException ep = (EasyPlatformWithLabelKeyException) ex;
                        _log.error("Run task :" + _ctx.getTaskId() + "->" + I18N.getLabel(ep.getMessage(), ep.getArgs()), ep);
                    } else
                        _log.error("Run task :" + _ctx.getTaskId(), ex);
                }
                if (_ctx.getCode() != null && _ctx.getCode().trim().length() > 1) {//处理错误逻辑
                    RecordContext rc = cc.getWorkflowContext().getRecord();
                    String code = RuntimeUtils.eval(cc, _ctx.getCode(), rc);
                    if (!"0000".equals(code)) {
                        if (_log.isWarnEnabled())
                            _log.warn("Run task ->{},execute code:", _ctx.getTaskId(), MessageUtils.byErrorCode(cc, rc, cc
                                    .getWorkflowContext().getId(), code));
                    }
                }
            } finally {
                if (_latch != null)
                    _latch.countDown();
                Contexts.clear();
                SecurityUtils.getSubject().logout();
            }
        }

    }

    @Override
    public IResponseMessage<?> execute(ExecuteContext ec) {
        CommandContext cc = ec.getCommandContext();
        WorkflowContext ctx = cc.getWorkflowContext();
        String code = null;
        if (ec.getCode() != null && ec.getCode().length() == 1)
            code = ec.getCode();
        TaskVo tv = new TaskVo(ec.getTaskId(), code);
        tv.setVariables(ec.getVariables());
        BeginRequestMessage req = new BeginRequestMessage(tv);
        req.setId(ctx != null ? ctx.getId() : null);//来源功能id
        BeginCmd beginCmd = new BeginCmd(req);
        IResponseMessage<?> resp = beginCmd.execute(cc);
        if (!resp.isSuccess())
            return resp;
        while (!(resp instanceof SimpleResponseMessage)) {
            AbstractPageVo pv = null;
            NextVo nv = new NextVo(null);
            if (resp instanceof PageResponseMessage) {
                pv = ((PageResponseMessage) resp).getBody();
                nv.setData(new HashMap<String, Object>());
            } else if (resp instanceof BatchResponseMessage) {
                pv = ((BatchResponseMessage) resp).getBody();
            }
            NextRequestMessage nreq = new NextRequestMessage(pv.getId(),
                    nv);
            NextCmd nextCmd = new NextCmd(nreq);
            resp = nextCmd.execute(cc);
            if (!resp.isSuccess())
                return resp;
        }
        return resp;
    }
}
